Skip to content

Conversation

@surminus
Copy link

@surminus surminus commented Feb 20, 2025

This implements ADR-119[1], which specifies the client connection options to update requests to the endpoints implemented as part of ADR-042[2].

The endpoint may be one of the following:

a routing policy name (such as main)
a nonprod routing policy name (such as nonprod:sandbox)
a FQDN such as foo.example.com
The endpoint option is not valid with any of environment, restHost or realtimeHost, but we still intend to support the legacy options.

If the client has been configured to use any of these legacy options, then they should continue to work in the same way, using the same primary and fallback hostnames.

If the client has not been explicitly configured, then the hostnames will change to the new ably.net domain when the package is upgraded.

Notes on implementation

As per previous guidance, I have not updated tests to use the endpoint client option, but since the hostnames are changing, then the assertions and default environment must be updated (from sandbox to nonprod:sandbox).

I have not added any explicit tests for the endpoint client option, but I can do this if we think it's necessary, although using environment is essentially the same as using endpoint.

See related Go and Ruby PRs:

[1] https://ably.atlassian.net/wiki/spaces/ENG/pages/3428810778/ADR-119+ClientOptions+for+new+DNS+structure
[2] https://ably.atlassian.net/wiki/spaces/ENG/pages/1791754276/ADR-042+DNS+Restructure

Summary by CodeRabbit

  • New Features

    • Added an endpoint option to select routing policies or provide a FQDN; default is "main".
  • Refactor

    • Unified host resolution and fallback generation around endpoint; environment, rest_host and realtime_host are deprecated and incompatible with endpoint.
  • Bug Fixes

    • Hostname and fallback generation now handle production, nonprod, custom FQDNs, IPv4/IPv6, and localhost correctly.
  • Tests

    • Updated and added tests to validate endpoint behavior, fallback lists, precedence, and expected URLs.
  • Documentation

    • Docs updated to mark deprecated options and document endpoint usage.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Feb 20, 2025

Walkthrough

Defaults and Options were converted to an endpoint-based model: Defaults exposes endpoint, get_hostname, and get_fallback_hosts; Options accepts and validates endpoint, resolves primary and fallback hosts via Defaults; many modules and tests updated to use get_host/get_hosts and endpoint.

Changes

Cohort / File(s) Change Summary
Defaults refactor
ably/transport/defaults.py
Removed fallback_hosts, rest_host, realtime_host, environment; added class attribute endpoint = 'main'; added static methods get_hostname(endpoint) and get_fallback_hosts(endpoint="main"); removed get_environment_fallback_hosts.
Options and host resolution
ably/types/options.py
Replaced environment/rest_host/realtime_host with canonical endpoint property; added fallback_host field and property; internal fields moved to __endpoint/__hosts/__fallback_host; new unified methods get_host/get_hosts/get_fallback_hosts centralize host resolution and delegate to Defaults; early validation prevents mixing endpoint with deprecated host options.
HTTP and transport integrations
ably/http/http.py, ably/realtime/connectionmanager.py, ably/transport/websockettransport.py
Consumers switched from get_rest_host/get_realtime_host and fallback_realtime_host to get_host/get_hosts and fallback_host; preferred/primary host selection and fallback caching now use the Options endpoint-based APIs.
Public constructors docs / params
ably/rest/rest.py, ably/realtime/realtime.py
Docstrings updated to document new endpoint kwarg and mark rest_host, realtime_host, and environment as deprecated.
Tests and test helpers updated
test/ably/*.py, test/unit/options_test.py, test/ably/rest/*.py, test/ably/realtime/*.py
Tests and test helpers changed to pass endpoint= instead of rest_host/realtime_host/environment; assertions and mocked URLs updated to use .realtime.ably.net / .realtime.ably-nonprod.net hosts; added comprehensive unit tests for Options host and fallback logic.
Http request host enumeration
ably/http/http.py
Http now exposes get_hosts() and uses options.get_hosts() in retry loops; fallback host checks compare against options.get_host().

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Options as Options
    participant Defaults as Defaults
    participant Http as HttpTransport

    Client->>Options: instantiate(endpoint or legacy args)
    Options->>Options: validate endpoint vs deprecated fields
    Options->>Defaults: get_hostname(endpoint)
    Defaults-->>Options: primary_host
    Options->>Defaults: get_fallback_hosts(endpoint)
    Defaults-->>Options: fallback_hosts[]
    Options-->>Http: provide get_host() / get_hosts()
    Http->>Options: get_hosts() for retries
    Http-->>Client: perform requests using resolved hosts
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

I'm a rabbit through endpoints, quick and keen,
I map hosts from roots both plain and nonprod green.
From "main" to custom names I hop and I find,
Five fallbacks sprout when routing is assigned.
(_/) 🥕 Endpoint set — onward, host resolved.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.81% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title accurately and concisely describes the main change: introducing endpoint as the default connection option, with ADR-119 reference providing specific context.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch laura/endpoint

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (2)
  • ADR-119: Request failed with status code 404
  • ADR-042: Request failed with status code 404

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot temporarily deployed to staging/pull/590/features February 20, 2025 16:16 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (6)
ably/transport/defaults.py (2)

47-57: Refine hostname derivation logic
These conditionals cover typical cases (FQDN, localhost, nonprod, main), but consider additional validations for custom endpoints or IPv6 addresses (beyond a simple :: check).


60-69: Evaluate fallback logic for different endpoint patterns
The fallback domain logic is straightforward for nonprod: and main, but consider whether the same approach is needed for other future or custom endpoint patterns.

Also applies to: 72-76

test/ably/rest/restinit_test.py (2)

79-79: Fix Yoda conditions in assertions.

The assertions use Yoda conditions which are not recommended in Python. Let's make them more readable.

-        assert "main.realtime.ably.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "main.realtime.ably.net", "Unexpected host mismatch %s" % host

-        assert "sandbox.realtime.ably-nonprod.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "sandbox.realtime.ably-nonprod.net", "Unexpected host mismatch %s" % host

Also applies to: 84-84

🧰 Tools
🪛 Ruff (0.8.2)

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


185-185: Fix line length and Yoda conditions in assertions.

The lines exceed the maximum line length of 115 characters and use Yoda conditions.

-        assert 'https://main.realtime.ably.net' == '{0}://{1}'.format(ably.http.preferred_scheme, ably.http.preferred_host)
+        expected_url = 'https://main.realtime.ably.net'
+        actual_url = '{0}://{1}'.format(ably.http.preferred_scheme, ably.http.preferred_host)
+        assert actual_url == expected_url

-        assert 'http://main.realtime.ably.net' == '{0}://{1}'.format(ably.http.preferred_scheme, ably.http.preferred_host)
+        expected_url = 'http://main.realtime.ably.net'
+        actual_url = '{0}://{1}'.format(ably.http.preferred_scheme, ably.http.preferred_host)
+        assert actual_url == expected_url

Also applies to: 191-191

🧰 Tools
🪛 Ruff (0.8.2)

185-185: Yoda condition detected

(SIM300)

🪛 GitHub Actions: Linting check

[error] 185-185: E501 line too long (123 > 115 characters)

ably/types/options.py (2)

12-13: Fix line length in constructor parameters.

The line exceeds the maximum line length of 115 characters.

-                 tls_port=0, use_binary_protocol=True, queue_messages=False, recover=False, endpoint=None,
-                 environment=None, http_open_timeout=None, http_request_timeout=None, realtime_request_timeout=None,
+                 tls_port=0, use_binary_protocol=True, queue_messages=False,
+                 recover=False, endpoint=None, environment=None,
+                 http_open_timeout=None, http_request_timeout=None,
+                 realtime_request_timeout=None,
🧰 Tools
🪛 GitHub Actions: Linting check

[error] 13-13: E501 line too long (116 > 115 characters)


286-290: Consider adding a docstring for the fallback hosts logic.

The fallback hosts logic has been updated to use endpoint-based configuration, but the logic could benefit from documentation explaining the behavior.

         fallback_hosts = self.fallback_hosts
         if fallback_hosts is None:
+            # If rest_host is explicitly set, don't use fallback hosts
+            # Otherwise, get fallback hosts based on the endpoint
             if self.rest_host:
                 fallback_hosts = []
             else:
                 fallback_hosts = Defaults.get_fallback_hosts(self.endpoint)
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7bd2ca and 3146e69.

📒 Files selected for processing (6)
  • ably/transport/defaults.py (2 hunks)
  • ably/types/options.py (6 hunks)
  • test/ably/rest/restinit_test.py (4 hunks)
  • test/ably/rest/restpaginatedresult_test.py (3 hunks)
  • test/ably/rest/restrequest_test.py (1 hunks)
  • test/ably/testapp.py (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • test/ably/rest/restrequest_test.py
🧰 Additional context used
🪛 Ruff (0.8.2)
test/ably/rest/restinit_test.py

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


84-84: Yoda condition detected

Rewrite as host == "sandbox.realtime.ably-nonprod.net"

(SIM300)


185-185: Yoda condition detected

(SIM300)


191-191: Yoda condition detected

(SIM300)

🪛 GitHub Actions: Linting check
test/ably/rest/restinit_test.py

[error] 185-185: E501 line too long (123 > 115 characters)


[error] 191-191: E501 line too long (122 > 115 characters)

ably/types/options.py

[error] 13-13: E501 line too long (116 > 115 characters)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: check (3.13)
  • GitHub Check: check (3.12)
  • GitHub Check: check (3.11)
  • GitHub Check: check (3.10)
  • GitHub Check: check (3.9)
  • GitHub Check: check (3.8)
🔇 Additional comments (9)
ably/transport/defaults.py (1)

5-5: Set default endpoint to 'main'
This aligns with ADR-119.

test/ably/rest/restpaginatedresult_test.py (4)

34-34: Update base_url to new default domain
Replacing rest.ably.io with main.realtime.ably.net is consistent with the new endpoint approach.


47-48: Update Link header references
Ensures the HTTP link headers match the new domain.


58-58: Use new domain in PaginatedResult query
Matches the endpoint logic in defaults and test configurations.


62-62: Adopt the updated domain for channel queries
Keeps consistency with the rest of the codebase changes.

test/ably/testapp.py (3)

17-18: Adopt nonprod host defaults
Replaces sandbox host references to point to sandbox.realtime.ably-nonprod.net, aligning test configuration with ADR-119 changes.


20-20: Switch environment default to 'nonprod:sandbox'
Reflects the new host naming convention and test alignment.


25-25: Verify conditional logic for custom rest_host values
When rest_host is not within realtime.ably-nonprod.net, the test harness forces ports to 8080/8081. Confirm this remains valid for future endpoint expansions.

ably/types/options.py (1)

45-57: LGTM! Good implementation of endpoint configuration.

The implementation correctly:

  1. Enforces mutual exclusivity between endpoint and environment
  2. Migrates environment value to endpoint if environment is provided
  3. Sets default endpoint from Defaults class

@github-actions github-actions bot temporarily deployed to staging/pull/590/features February 20, 2025 16:28 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🔭 Outside diff range comments (1)
test/ably/rest/restinit_test.py (1)

184-221: 🛠️ Refactor suggestion

Fix Yoda conditions and update environment to endpoint.

Update the test to use endpoint instead of environment and fix Yoda conditions.

-        assert 'https://main.realtime.ably.net' == '{0}://{1}'.format(
+        assert '{0}://{1}'.format(
             ably.http.preferred_scheme, ably.http.preferred_host
-        )
+        ) == 'https://main.realtime.ably.net'

-        assert 'http://main.realtime.ably.net' == '{0}://{1}'.format(
+        assert '{0}://{1}'.format(
             ably.http.preferred_scheme, ably.http.preferred_host
-        )
+        ) == 'http://main.realtime.ably.net'

-        ably = AblyRest(token='token', environment='custom')
+        ably = AblyRest(token='token', endpoint='custom')
🧰 Tools
🪛 Ruff (0.8.2)

185-187: Yoda condition detected

(SIM300)


194-196: Yoda condition detected

(SIM300)


207-207: Yoda condition detected

Rewrite as excinfo.value.status_code == 401

(SIM300)


208-208: Yoda condition detected

Rewrite as excinfo.value.code == 40103

(SIM300)


209-209: Yoda condition detected

(SIM300)


215-218: Use contextlib.suppress(AblyException) instead of try-except-pass

Replace with contextlib.suppress(AblyException)

(SIM105)

🧹 Nitpick comments (3)
ably/transport/defaults.py (1)

60-77: Consider adding docstring to document the fallback host pattern.

The method correctly implements the fallback host generation logic but would benefit from documentation explaining the pattern used for both production and non-production environments.

 @staticmethod
 def get_fallback_hosts(endpoint="main"):
+    """Generate a list of fallback hosts based on the endpoint.
+    
+    Args:
+        endpoint (str, optional): The endpoint to generate fallback hosts for. Defaults to "main".
+            Can be a routing policy name (e.g., "main"), a non-production routing policy name
+            (e.g., "nonprod:sandbox"), or a custom endpoint.
+    
+    Returns:
+        list[str]: A list of fallback hostnames following the pattern:
+            - For nonprod: <root>.{a-e}.fallback.ably-realtime-nonprod.com
+            - For others: <endpoint>.{a-e}.fallback.ably-realtime.com
+    """
ably/types/options.py (1)

173-174: Consider adding setter for endpoint property.

While the current implementation treats endpoint as read-only, consider adding a setter to allow runtime modifications, similar to other properties in the class.

 @property
 def endpoint(self):
     return self.__endpoint
+
+@endpoint.setter
+def endpoint(self, value):
+    self.__endpoint = value
+    self.__rest_hosts = self.__get_rest_hosts()
+    self.__realtime_hosts = self.__get_realtime_hosts()
test/ably/rest/restinit_test.py (1)

79-80: Fix Yoda condition.

Rewrite the condition to improve readability.

-        assert "main.realtime.ably.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "main.realtime.ably.net", "Unexpected host mismatch %s" % host
🧰 Tools
🪛 Ruff (0.8.2)

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3146e69 and 3c1a33d.

📒 Files selected for processing (6)
  • ably/transport/defaults.py (2 hunks)
  • ably/types/options.py (6 hunks)
  • test/ably/rest/restinit_test.py (4 hunks)
  • test/ably/rest/restpaginatedresult_test.py (3 hunks)
  • test/ably/rest/restrequest_test.py (1 hunks)
  • test/ably/testapp.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/ably/rest/restpaginatedresult_test.py
  • test/ably/rest/restrequest_test.py
  • test/ably/testapp.py
🧰 Additional context used
🪛 Ruff (0.8.2)
test/ably/rest/restinit_test.py

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


84-84: Yoda condition detected

Rewrite as host == "sandbox.realtime.ably-nonprod.net"

(SIM300)


185-187: Yoda condition detected

(SIM300)


194-196: Yoda condition detected

(SIM300)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: check (3.13)
  • GitHub Check: check (3.12)
  • GitHub Check: check (3.11)
  • GitHub Check: check (3.10)
  • GitHub Check: check (3.9)
  • GitHub Check: check (3.8)
🔇 Additional comments (6)
ably/transport/defaults.py (2)

5-5: LGTM!

The default endpoint value 'main' aligns with ADR-119 specifications.


47-58: LGTM! Well-structured hostname resolution logic.

The get_hostname method correctly handles all endpoint formats specified in ADR-119:

  • Fully qualified domain names (contains ".")
  • IPv6 addresses (contains "::")
  • Localhost
  • Non-production routing policy (starts with "nonprod:")
  • Production routing policy ("main")
  • Custom routing policy (fallback case)
ably/types/options.py (4)

12-18: LGTM! Constructor signature updated correctly.

The endpoint parameter has been added while maintaining backward compatibility with environment.


46-58: LGTM! Robust validation and fallback logic.

The code correctly:

  • Prevents simultaneous use of endpoint and environment
  • Falls back to environment value if endpoint is not provided
  • Uses default endpoint from Defaults class as final fallback

277-291: LGTM! Host resolution logic updated correctly.

The code properly handles:

  • Custom rest_host override
  • Endpoint-based hostname resolution
  • Fallback host generation based on endpoint

307-308: LGTM! Realtime host resolution updated.

The code correctly uses the new endpoint-based hostname resolution for realtime hosts.

@github-actions github-actions bot temporarily deployed to staging/pull/590/features February 20, 2025 16:51 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
ably/types/options.py (1)

176-177: Add endpoint property documentation.

Consider adding docstring to the endpoint property to describe its purpose and valid values.

 @property
 def endpoint(self):
+    """
+    The endpoint used for host resolution. Can be:
+    - A routing policy name (e.g., "main")
+    - A non-production routing policy (e.g., "nonprod:sandbox")
+    - A fully qualified domain name (e.g., "foo.example.com")
+    """
     return self.__endpoint
test/ably/rest/restinit_test.py (3)

79-79: Fix Yoda conditions in assertions.

Update the assertions to follow Python's conventional comparison order.

-        assert "main.realtime.ably.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "main.realtime.ably.net", "Unexpected host mismatch %s" % host
-        assert "sandbox.realtime.ably-nonprod.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "sandbox.realtime.ably-nonprod.net", "Unexpected host mismatch %s" % host

Also applies to: 84-84

🧰 Tools
🪛 Ruff (0.8.2)

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


106-108: Update environment to endpoint in test.

For consistency with ADR-119, update the test to use endpoint instead of environment.

-        ably = AblyRest(token='foo', environment='nonprod:sandbox', http_max_retry_count=10)
+        ably = AblyRest(token='foo', endpoint='nonprod:sandbox', http_max_retry_count=10)
         assert sorted(Defaults.get_fallback_hosts('nonprod:sandbox')) == sorted(
             ably.options.get_fallback_rest_hosts())

185-187: Fix Yoda conditions in URL assertions.

Update the URL assertions to follow Python's conventional comparison order.

-        assert 'https://main.realtime.ably.net' == '{0}://{1}'.format(
+        assert '{0}://{1}'.format(
             ably.http.preferred_scheme, ably.http.preferred_host
-        )
+        ) == 'https://main.realtime.ably.net'

-        assert 'http://main.realtime.ably.net' == '{0}://{1}'.format(
+        assert '{0}://{1}'.format(
             ably.http.preferred_scheme, ably.http.preferred_host
-        )
+        ) == 'http://main.realtime.ably.net'

Also applies to: 194-196

🧰 Tools
🪛 Ruff (0.8.2)

185-187: Yoda condition detected

(SIM300)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3c1a33d and d9706dd.

📒 Files selected for processing (6)
  • ably/transport/defaults.py (2 hunks)
  • ably/types/options.py (6 hunks)
  • test/ably/rest/restinit_test.py (4 hunks)
  • test/ably/rest/restpaginatedresult_test.py (3 hunks)
  • test/ably/rest/restrequest_test.py (1 hunks)
  • test/ably/testapp.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • test/ably/rest/restrequest_test.py
  • test/ably/rest/restpaginatedresult_test.py
  • test/ably/testapp.py
🧰 Additional context used
🪛 Ruff (0.8.2)
test/ably/rest/restinit_test.py

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


84-84: Yoda condition detected

Rewrite as host == "sandbox.realtime.ably-nonprod.net"

(SIM300)


185-187: Yoda condition detected

(SIM300)


194-196: Yoda condition detected

(SIM300)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: check (3.13)
  • GitHub Check: check (3.12)
  • GitHub Check: check (3.11)
  • GitHub Check: check (3.10)
  • GitHub Check: check (3.9)
  • GitHub Check: check (3.8)
🔇 Additional comments (7)
ably/transport/defaults.py (3)

5-5: LGTM! Endpoint configuration aligns with ADR-119.

The default endpoint is set to 'main', which is the correct default for production environments.


47-58: LGTM! Hostname resolution logic is well-structured.

The get_hostname method correctly handles all endpoint formats specified in ADR-119:

  • Fully qualified domain names (contains ".")
  • IPv6 addresses (contains "::")
  • Localhost
  • Non-production routing policy (starts with "nonprod:")
  • Production routing policy ("main")
  • Custom endpoints

60-77: LGTM! Fallback host generation is comprehensive.

The get_fallback_hosts method correctly generates fallback hosts based on the endpoint type:

  • Non-production endpoints use the ably-realtime-nonprod.com domain
  • Production endpoints use the ably-realtime.com domain
ably/types/options.py (4)

12-18: LGTM! Constructor parameters updated correctly.

The endpoint parameter has been added while maintaining backward compatibility with the environment parameter.


46-48: LGTM! Mutual exclusivity check added.

Correctly enforces mutual exclusivity between endpoint and environment parameters as specified in ADR-119.


53-61: LGTM! Environment to endpoint transition logic is correct.

The code properly handles the transition from environment to endpoint:

  1. Maps "production" environment to default endpoint
  2. Uses environment value as endpoint if endpoint is not specified
  3. Falls back to default endpoint if neither is specified

280-281: LGTM! Host resolution logic updated correctly.

The code properly uses the new endpoint-based host resolution while maintaining backward compatibility with rest_host.

Also applies to: 289-294

Copy link
Contributor

@VeskeR VeskeR left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some comments about implementation details.

Some other things:

Given the impact of this changes, I would also like to see some endpoint tests (could be simple unit tests) to check that providing a new endpoint option results in the expected rest host, realtime host and fallback hosts used.

  • can check that providing incomatible options (e.g. endpoint with environment) results in the error.

I can also see that the ably-go PR deprecated the restHost, realtimeHost and environment options: https://github.com/ably/ably-go/pull/679/files#diff-721ab670921034c0c26f7999ad630004f73001cb85be84c768e7bf93c6abf14fR256-R280.
Should we do the same here, or can create a separate ticket for it? Don't know if there was an explicit decision to deprecate those.

test/ably/rest/resthttp_test.py::TestRestHttp::test_request_headers - test consistently fails after changes, should investigate

@surminus
Copy link
Author

surminus commented Mar 3, 2025

Great, thank you for the review.

Given the impact of this changes, I would also like to see some endpoint tests (could be simple unit tests) to check that providing a new endpoint option results in the expected rest host, realtime host and fallback hosts used.

I will add these tests.

Should we do the same here, or can create a separate ticket for it? Don't know if there was an explicit decision to deprecate those.

I can do that as part of this PR, that's fine. I'll also need to update my ably-js and ably-ruby PRs.

@VeskeR
Copy link
Contributor

VeskeR commented Mar 4, 2025

Just a heads up:

test/ably/rest/resthttp_test.py::TestRestHttp::test_request_headers - test consistently fails after changes, should investigate

This is not related to this PR, fixed in #593. You can ignore test_request_headers test failures for the time being

@github-actions github-actions bot temporarily deployed to staging/pull/590/features March 6, 2025 18:10 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (2)
test/ably/rest/restinit_test.py (2)

106-112: 🛠️ Refactor suggestion

Update environment to endpoint in fallback hosts test.

For consistency with ADR-119, update the test to use endpoint instead of environment.

-        ably = AblyRest(token='foo', environment='nonprod:sandbox', http_max_retry_count=10)
+        ably = AblyRest(token='foo', endpoint='nonprod:sandbox', http_max_retry_count=10)
        assert sorted(Defaults.get_fallback_hosts('nonprod:sandbox')) == sorted(
            ably.options.get_fallback_rest_hosts())

76-84: 🛠️ Refactor suggestion

Fix Yoda conditions and update environment to endpoint.

The tests use environment parameters but should be updated to use endpoint parameters for consistency with ADR-119. Also, fix the Yoda conditions flagged by the static analyzer.

        # environment: main
-        ably = AblyRest(token='foo', environment="main")
+        ably = AblyRest(token='foo', endpoint="main")
        host = ably.options.get_rest_host()
-        assert "main.realtime.ably.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "main.realtime.ably.net", "Unexpected host mismatch %s" % host

        # environment: other
-        ably = AblyRest(token='foo', environment="nonprod:sandbox")
+        ably = AblyRest(token='foo', endpoint="nonprod:sandbox")
        host = ably.options.get_rest_host()
-        assert "sandbox.realtime.ably-nonprod.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "sandbox.realtime.ably-nonprod.net", "Unexpected host mismatch %s" % host
🧰 Tools
🪛 Ruff (0.8.2)

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


84-84: Yoda condition detected

Rewrite as host == "sandbox.realtime.ably-nonprod.net"

(SIM300)

🧹 Nitpick comments (5)
ably/realtime/realtime.py (1)

51-59: Documentation improvements needed.

The endpoint parameter is structured correctly, but the deprecated parameters documentation could be improved for better readability and consistency with the rest.py file.

             endpoint: str
                 Endpoint specifies either a routing policy name or fully qualified domain name to connect to Ably.
             realtime_host: str
-                Deprecated: this property is deprecated and will be removed in a future version.
-                Enables a non-default Ably host to be specified for realtime connections.
-                For development environments only. The default value is realtime.ably.io.
+                Deprecated: this property is deprecated and will be removed in a future version. Enables a non-default 
+                Ably host to be specified for realtime connections. For development environments only. 
+                The default value is realtime.ably.io.
             environment: str
-                Deprecated: this property is deprecated and will be removed in a future version.
-                Enables a custom environment to be used with the Ably service. Defaults to `production`
+                Deprecated: this property is deprecated and will be removed in a future version. Enables a custom 
+                environment to be used with the Ably service. Defaults to `production`
test/ably/rest/restinit_test.py (2)

185-187: Fix Yoda condition in HTTPS assertion.

-        assert 'https://main.realtime.ably.net' == '{0}://{1}'.format(
-            ably.http.preferred_scheme, ably.http.preferred_host
-        )
+        assert '{0}://{1}'.format(
+            ably.http.preferred_scheme, ably.http.preferred_host
+        ) == 'https://main.realtime.ably.net'
🧰 Tools
🪛 Ruff (0.8.2)

185-187: Yoda condition detected

(SIM300)


194-196: Fix Yoda condition in HTTP assertion.

-        assert 'http://main.realtime.ably.net' == '{0}://{1}'.format(
-            ably.http.preferred_scheme, ably.http.preferred_host
-        )
+        assert '{0}://{1}'.format(
+            ably.http.preferred_scheme, ably.http.preferred_host
+        ) == 'http://main.realtime.ably.net'
🧰 Tools
🪛 Ruff (0.8.2)

194-196: Yoda condition detected

(SIM300)

ably/types/options.py (2)

287-290: Condition should check explicitly if rest_host is None.

Using if self.rest_host is not None: is not as clear as an explicit comparison to None. This pattern is also inconsistent with other checks in the codebase.

-            if self.rest_host is not None:
+            if self.rest_host is not None:
                 fallback_hosts = []
             else:
                 fallback_hosts = Defaults.get_fallback_hosts(self.endpoint)

173-174: Consider adding an endpoint.setter method.

All other properties in this class have both getter and setter methods. For consistency, it would be good to add a setter for the endpoint property as well, even if it's not currently needed.

@property
def endpoint(self):
    return self.__endpoint

+@endpoint.setter
+def endpoint(self, value):
+    self.__endpoint = value
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between d9706dd and b04697e.

📒 Files selected for processing (5)
  • ably/realtime/realtime.py (1 hunks)
  • ably/rest/rest.py (1 hunks)
  • ably/transport/defaults.py (2 hunks)
  • ably/types/options.py (6 hunks)
  • test/ably/rest/restinit_test.py (4 hunks)
🧰 Additional context used
🪛 GitHub Actions: Linting check
ably/rest/rest.py

[error] 36-38: E501 line too long (122 > 115 characters)


[error] 37-37: E501 line too long (154 > 115 characters)


[error] 38-38: E501 line too long (156 > 115 characters)

ably/types/options.py

[error] 46-46: E501 line too long (116 > 115 characters)


[error] 47-47: E117 over-indented

🪛 Ruff (0.8.2)
test/ably/rest/restinit_test.py

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


84-84: Yoda condition detected

Rewrite as host == "sandbox.realtime.ably-nonprod.net"

(SIM300)


185-187: Yoda condition detected

(SIM300)


194-196: Yoda condition detected

(SIM300)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: check (3.13)
  • GitHub Check: check (3.12)
  • GitHub Check: check (3.11)
  • GitHub Check: check (3.10)
  • GitHub Check: check (3.9)
  • GitHub Check: check (3.8)
🔇 Additional comments (5)
ably/transport/defaults.py (1)

3-5: Implementation looks good.

The changes to replace environment with endpoint and set the default to 'main' align with ADR-119 requirements.

ably/types/options.py (4)

12-14: The new endpoint parameter implementation looks good.

Adding the endpoint parameter and maintaining backward compatibility with the environment parameter aligns with the objectives of ADR-119.


278-278:

Details

✅ Verification successful

Verify that the Defaults.get_hostname and Defaults.get_fallback_hosts methods exist.

The code now calls two new methods from the Defaults class: get_hostname and get_fallback_hosts. Let's verify that these methods exist and work as expected.

Also applies to: 290-290, 307-307


🏁 Script executed:

#!/bin/bash
# Check for the implementation of get_hostname and get_fallback_hosts in Defaults
rg -A 10 "def get_(hostname|fallback_hosts)" --type py ably/transport/defaults.py

Length of output: 923


Verification Complete: Defaults Methods Implementation Verified

Both Defaults.get_hostname and Defaults.get_fallback_hosts have been confirmed to exist and are correctly implemented in ably/transport/defaults.py. The usage in ably/types/options.py (lines 278, 290, and 307) is valid with no issues detected.


46-47:

Details

❓ Verification inconclusive

Verify that all legacy options are properly validated against endpoint.

The current validation checks for incompatibility with environment, rest_host, and realtime_host, but based on previous comments, we should also check for compatibility with fallback_hosts_use_default as implemented in ably-go.


🏁 Script executed:

#!/bin/bash
# Check if fallback_hosts_use_default is mentioned in the PR or the defaults.py file
rg -i "fallback_hosts_use_default" --type py

Length of output: 44


Action Required: Validate legacy option "fallback_hosts_use_default" against endpoint

It appears that while the validation in ably/types/options.py currently checks for incompatibility between endpoint and the options environment, rest_host, and realtime_host, there is no check for fallback_hosts_use_default. Our previous shell search did not reveal any references to fallback_hosts_use_default in the repository. Please verify manually whether this legacy option should also be validated (as in ably-go) and, if so, update the logic accordingly. For example, you might consider modifying the conditional to:

if endpoint is not None and (environment is not None or rest_host is not None or realtime_host is not None or fallback_hosts_use_default is not None):
    raise ValueError('endpoint is incompatible with any of environment, rest_host, realtime_host, or fallback_hosts_use_default')

Please double-check the correct parameter name and type before making changes.

🧰 Tools
🪛 GitHub Actions: Linting check

[error] 46-46: E501 line too long (116 > 115 characters)


[error] 47-47: E117 over-indented


53-57:

Details

❓ Verification inconclusive

Consider adding special handling for 'production' environment.

There appears to be a discrepancy between the ably-go implementation and this one regarding how the 'production' environment is handled. In a previous review comment, it was noted that ably-go has special handling for the 'production' environment, but this implementation doesn't seem to have it.


🏁 Script executed:

#!/bin/bash
# Check how 'production' is handled in the Defaults class and any other related files
rg -A 5 -B 5 "production" --type py ably/transport/defaults.py

Length of output: 62


Action: Verify and Align 'production' Environment Handling

In the current implementation in ably/types/options.py (lines 53–57), if an environment is provided without an explicit endpoint, the code directly assigns endpoint = environment—even when the value is "production". This contrasts with the ably-go implementation, which applies special handling for the production environment.

  • A search in ably/transport/defaults.py for the term "production" did not reveal any special-case logic.
  • Please verify whether the Python client should also implement explicit handling for a "production" environment (for instance, mapping it to a specific default production URL) to mirror the ably-go behavior, or if the current behavior is intentional and the documentation should be updated accordingly.

@github-actions github-actions bot temporarily deployed to staging/pull/590/features March 11, 2025 16:32 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
test/ably/rest/restinit_test.py (3)

79-79: Fix Yoda condition

The assertion is using a Yoda condition (placing the literal first in a comparison).

-        assert "main.realtime.ably.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "main.realtime.ably.net", "Unexpected host mismatch %s" % host
🧰 Tools
🪛 Ruff (0.8.2)

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


82-84: Properly updated environment to nonprod:sandbox with Yoda condition

The test has been correctly updated to use the new "nonprod:sandbox" environment, which aligns with the hostname changes mentioned in the PR objectives.

However, there's another Yoda condition that should be fixed:

-        assert "sandbox.realtime.ably-nonprod.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "sandbox.realtime.ably-nonprod.net", "Unexpected host mismatch %s" % host
🧰 Tools
🪛 Ruff (0.8.2)

84-84: Yoda condition detected

Rewrite as host == "sandbox.realtime.ably-nonprod.net"

(SIM300)


185-196: Fix Yoda conditions in HTTPS/HTTP tests

Both the HTTPS and HTTP tests use Yoda conditions.

For HTTPS test:

-        assert 'https://main.realtime.ably.net' == '{0}://{1}'.format(
-            ably.http.preferred_scheme, ably.http.preferred_host
-        )
+        assert '{0}://{1}'.format(
+            ably.http.preferred_scheme, ably.http.preferred_host
+        ) == 'https://main.realtime.ably.net'

For HTTP test:

-        assert 'http://main.realtime.ably.net' == '{0}://{1}'.format(
-            ably.http.preferred_scheme, ably.http.preferred_host
-        )
+        assert '{0}://{1}'.format(
+            ably.http.preferred_scheme, ably.http.preferred_host
+        ) == 'http://main.realtime.ably.net'
🧰 Tools
🪛 Ruff (0.8.2)

185-187: Yoda condition detected

(SIM300)


194-196: Yoda condition detected

(SIM300)

ably/types/options.py (1)

46-48: Validation for endpoint vs. environment is correct but could be simplified

The validation is correct but could be simplified by using a single if statement when checking for incompatible options.

-        if environment is not None and endpoint is not None:
-            raise ValueError('specify endpoint or environment, not both')
+        if endpoint is not None and (environment is not None or rest_host is not None or realtime_host is not None):
+            raise ValueError('endpoint is incompatible with any of environment, rest_host or realtime_host')

This approach would also align with the ADR-119 requirements by checking that endpoint cannot be used with any of the legacy options (environment, restHost, or realtimeHost).

🧰 Tools
🪛 Ruff (0.8.2)

46-47: Use a single if statement instead of nested if statements

(SIM102)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between b04697e and 485ce5b.

📒 Files selected for processing (11)
  • ably/transport/defaults.py (2 hunks)
  • ably/types/options.py (6 hunks)
  • test/ably/rest/restinit_test.py (4 hunks)
  • test/ably/rest/restpaginatedresult_test.py (3 hunks)
  • test/ably/rest/restrequest_test.py (1 hunks)
  • test/ably/testapp.py (1 hunks)
  • ably/realtime/realtime.py (1 hunks)
  • ably/rest/rest.py (1 hunks)
  • ably/transport/defaults.py (1 hunks)
  • ably/types/options.py (2 hunks)
  • test/ably/rest/restinit_test.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • test/ably/rest/restrequest_test.py
  • test/ably/rest/restpaginatedresult_test.py
  • test/ably/testapp.py
  • ably/rest/rest.py
  • ably/types/options.py
  • test/ably/rest/restinit_test.py
  • ably/transport/defaults.py
🧰 Additional context used
🪛 Ruff (0.8.2)
ably/types/options.py

46-47: Use a single if statement instead of nested if statements

(SIM102)

test/ably/rest/restinit_test.py

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


84-84: Yoda condition detected

Rewrite as host == "sandbox.realtime.ably-nonprod.net"

(SIM300)


185-187: Yoda condition detected

(SIM300)


194-196: Yoda condition detected

(SIM300)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: check (3.13)
  • GitHub Check: check (3.12)
  • GitHub Check: check (3.11)
  • GitHub Check: check (3.10)
  • GitHub Check: check (3.9)
  • GitHub Check: check (3.8)
🔇 Additional comments (6)
ably/realtime/realtime.py (1)

51-59: New endpoint parameter correctly implemented with deprecation notices

The addition of the endpoint parameter is well-implemented, with clear documentation about its purpose. Good job marking the realtime_host and environment parameters as deprecated with clear notifications that they will be removed in a future version. This aligns perfectly with the ADR-119 requirements mentioned in the PR objectives.

ably/transport/defaults.py (2)

47-57: Well-structured hostname resolution logic

The get_hostname method properly handles various endpoint formats as specified in ADR-119:

  • FQDNs (containing "." or "::" or "localhost")
  • Non-production routing policies (starting with "nonprod:")
  • Default "main" endpoint
  • Other endpoints

The implementation is clean and follows the requirements correctly.


60-77: Verify REC2c2 fallback behavior implementation

While the fallback host generation is implemented correctly, the code appears to be missing the full REC2c2-compliant fallback behavior. According to the specification, besides generating fallback host addresses, the SDK should also implement a robust retry mechanism.

Please verify that the REC2c2 implementation includes:

  • Logic to iterate through fallback hosts when the primary host is unreachable
  • A timeout mechanism to restrict fallback retries
  • Correct toggling between production and non-production endpoint formats

Reviewing the ably-go implementation referenced in previous comments would help ensure full compliance with REC2c2.

test/ably/rest/restinit_test.py (1)

106-112: Consider updating environment to endpoint

The test is still using environment parameter instead of the new endpoint parameter. For consistency with ADR-119, it would be better to update the test to use the endpoint option.

-        ably = AblyRest(token='foo', environment='nonprod:sandbox', http_max_retry_count=10)
+        ably = AblyRest(token='foo', endpoint='nonprod:sandbox', http_max_retry_count=10)
         assert sorted(Defaults.get_fallback_hosts('nonprod:sandbox')) == sorted(
             ably.options.get_fallback_rest_hosts())
ably/types/options.py (2)

53-60: Good logic for handling endpoint defaults

The code correctly:

  1. Maps "production" environment to the default endpoint
  2. Falls back to using environment as endpoint if no endpoint is provided
  3. Uses the default endpoint when neither is provided

This implementation handles backward compatibility while encouraging the use of the new endpoint parameter.


290-294: Proper fallback hosts selection based on rest_host and endpoint

The logic correctly determines whether to use custom fallback hosts based on whether a custom rest_host is provided. If not, it uses the fallback hosts based on the endpoint.

@github-actions github-actions bot temporarily deployed to staging/pull/590/features March 11, 2025 16:55 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
ably/transport/defaults.py (1)

47-58: ⚠️ Potential issue

FQDN handling in get_hostname needs REC2c2 implementation.

The hostname resolution logic is well-structured for different endpoint formats, but appears to be missing implementation of REC2c2 requirements for fallback behavior. This was also highlighted in previous reviews.

#!/bin/bash
# Description: Check the Ably specification for REC2c2 requirements

# Lookup ably-go implementation for comparison
rg -A 10 "REC2c2" --glob "*.py"

# Check if there's any retry/fallback logic implemented
rg -A 5 "fallback_retry_timeout" --glob "*.py"
🧹 Nitpick comments (3)
test/ably/rest/restinit_test.py (2)

79-79: Fix Yoda condition and update hostname assertion.

The hostname assertion has been correctly updated to reflect the new main.realtime.ably.net domain, but uses a non-preferred Yoda condition format.

-        assert "main.realtime.ably.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "main.realtime.ably.net", "Unexpected host mismatch %s" % host
🧰 Tools
🪛 Ruff (0.8.2)

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


185-187: Fix Yoda conditions in URL assertions.

The URL assertions have been correctly updated to reflect the new main.realtime.ably.net domain, but use non-preferred Yoda condition format.

-        assert 'https://main.realtime.ably.net' == '{0}://{1}'.format(
+        assert '{0}://{1}'.format(
+            ably.http.preferred_scheme, ably.http.preferred_host
+        ) == 'https://main.realtime.ably.net'

-        assert 'http://main.realtime.ably.net' == '{0}://{1}'.format(
+        assert '{0}://{1}'.format(
+            ably.http.preferred_scheme, ably.http.preferred_host
+        ) == 'http://main.realtime.ably.net'

Also applies to: 194-196

🧰 Tools
🪛 Ruff (0.8.2)

185-187: Yoda condition detected

(SIM300)

ably/types/options.py (1)

46-48: Redundant validation check.

This validation check is redundant as it's already covered by the broader check on lines 22-24.

-        if environment is not None and endpoint is not None:
-            raise ValueError('specify endpoint or environment, not both')
-
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 485ce5b and 7e99ed3.

📒 Files selected for processing (8)
  • ably/transport/defaults.py (2 hunks)
  • ably/types/options.py (6 hunks)
  • test/ably/rest/restinit_test.py (4 hunks)
  • ably/realtime/realtime.py (1 hunks)
  • ably/rest/rest.py (1 hunks)
  • ably/transport/defaults.py (1 hunks)
  • ably/types/options.py (2 hunks)
  • test/ably/rest/restinit_test.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • ably/rest/rest.py
  • test/ably/rest/restinit_test.py
  • ably/types/options.py
🧰 Additional context used
🪛 Ruff (0.8.2)
test/ably/rest/restinit_test.py

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


84-84: Yoda condition detected

Rewrite as host == "sandbox.realtime.ably-nonprod.net"

(SIM300)


185-187: Yoda condition detected

(SIM300)


194-196: Yoda condition detected

(SIM300)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: check (3.13)
  • GitHub Check: check (3.12)
  • GitHub Check: check (3.11)
  • GitHub Check: check (3.10)
  • GitHub Check: check (3.9)
  • GitHub Check: check (3.8)
🔇 Additional comments (12)
ably/realtime/realtime.py (2)

51-53: Good documentation for the new endpoint parameter.

The documentation clearly explains the purpose of the endpoint parameter and its valid formats (routing policy name or FQDN) as specified in ADR-119.


54-59: Effective deprecation notices for legacy parameters.

Properly marking realtime_host and environment as deprecated with clear messaging that they'll be removed in future versions aligns with the PR objectives of transitioning to endpoint-based configuration.

ably/transport/defaults.py (2)

5-5: Appropriate default endpoint value.

Setting the default endpoint to 'main' aligns with ADR-119 and ensures backward compatibility when upgrading the package.


60-77: Well-structured fallback hosts generation.

The implementation correctly generates appropriate fallback hosts based on the endpoint type (production vs. non-production) as required in ADR-119.

test/ably/rest/restinit_test.py (3)

82-84: Update environment to endpoint and fix Yoda condition.

The test correctly uses the new nonprod:sandbox format, but should be updated to use the endpoint parameter instead of environment for consistency with ADR-119. Also fix the Yoda condition.

-        ably = AblyRest(token='foo', environment="nonprod:sandbox")
+        ably = AblyRest(token='foo', endpoint="nonprod:sandbox")
         host = ably.options.get_rest_host()
-        assert "sandbox.realtime.ably-nonprod.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "sandbox.realtime.ably-nonprod.net", "Unexpected host mismatch %s" % host
🧰 Tools
🪛 Ruff (0.8.2)

84-84: Yoda condition detected

Rewrite as host == "sandbox.realtime.ably-nonprod.net"

(SIM300)


106-112: Update environment to endpoint in fallback hosts test.

For consistency with ADR-119, update the test to use endpoint instead of environment.

-        ably = AblyRest(token='foo', environment='nonprod:sandbox', http_max_retry_count=10)
+        ably = AblyRest(token='foo', endpoint='nonprod:sandbox', http_max_retry_count=10)
         assert sorted(Defaults.get_fallback_hosts('nonprod:sandbox')) == sorted(
             ably.options.get_fallback_rest_hosts())

220-220: Updated endpoint URL for environment test.

The test correctly uses the new domain format for custom environments.

ably/types/options.py (5)

12-13: Added endpoint parameter to constructor.

The endpoint parameter has been properly added to the Options constructor, implementing the changes required by ADR-119.


22-24: Good validation for mutually exclusive parameters.

The validation ensures endpoint cannot be used with legacy options as required by ADR-119.

🧰 Tools
🪛 Ruff (0.8.2)

22-23: Use a single if statement instead of nested if statements

(SIM102)


53-61: Logical endpoint assignment flow.

The endpoint assignment logic correctly handles all specified cases and defaults, ensuring backward compatibility while implementing the new endpoint-based approach.


176-177: Transition from environment to endpoint property.

Good replacement of the environment property with endpoint to reflect the architectural change.


289-294: Improved fallback hosts logic.

The code correctly determines whether to use custom fallback hosts or generate them based on the endpoint.

@github-actions github-actions bot temporarily deployed to staging/pull/590/features March 11, 2025 17:46 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/590/features March 11, 2025 17:50 Inactive
@surminus
Copy link
Author

@VeskeR @sacOO7 thanks for reviews, I've updated this PR with a fixup that adds some (very basic) tests, ensures client combinations are incompatible, and addresses code comments.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
test/ably/rest/restinit_test.py (3)

79-79: Fix Yoda condition.

Rewrite the condition to follow standard Python style.

-        assert "main.realtime.ably.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "main.realtime.ably.net", "Unexpected host mismatch %s" % host
🧰 Tools
🪛 Ruff (0.8.2)

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


185-187: Fix Yoda condition in HTTPS URL assertion.

Rewrite condition to follow standard Python style.

-        assert 'https://main.realtime.ably.net' == '{0}://{1}'.format(
+        assert '{0}://{1}'.format(
             ably.http.preferred_scheme, ably.http.preferred_host
-        )
+        ) == 'https://main.realtime.ably.net'
🧰 Tools
🪛 Ruff (0.8.2)

185-187: Yoda condition detected

(SIM300)


194-196: Fix Yoda condition in HTTP URL assertion.

Rewrite condition to follow standard Python style.

-        assert 'http://main.realtime.ably.net' == '{0}://{1}'.format(
+        assert '{0}://{1}'.format(
             ably.http.preferred_scheme, ably.http.preferred_host
-        )
+        ) == 'http://main.realtime.ably.net'
🧰 Tools
🪛 Ruff (0.8.2)

194-196: Yoda condition detected

(SIM300)

ably/types/options.py (1)

289-311: Ensure consistent fallback host handling in realtime hosts.

The code checks for self.rest_host when determining fallback hosts for REST, but doesn't do an equivalent check for realtime hosts. Consider making this behavior consistent.

    def __get_realtime_hosts(self):
        if self.realtime_host is not None:
            host = self.realtime_host
            return [host]

        host = Defaults.get_hostname(self.endpoint)
-        return [host] + self.__fallback_hosts
+        # Fallback hosts
+        fallback_hosts = self.fallback_hosts
+        if fallback_hosts is None:
+            if self.realtime_host:
+                fallback_hosts = []
+            else:
+                fallback_hosts = Defaults.get_fallback_hosts(self.endpoint)
+        
+        return [host] + fallback_hosts
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between e4b87b4 and 9bd9857.

📒 Files selected for processing (9)
  • ably/transport/defaults.py (2 hunks)
  • ably/types/options.py (6 hunks)
  • test/ably/rest/restinit_test.py (4 hunks)
  • ably/realtime/realtime.py (1 hunks)
  • ably/rest/rest.py (1 hunks)
  • ably/transport/defaults.py (1 hunks)
  • ably/types/options.py (2 hunks)
  • test/ably/rest/restinit_test.py (1 hunks)
  • test/unit/options_test.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (5)
  • ably/transport/defaults.py
  • ably/realtime/realtime.py
  • ably/types/options.py
  • ably/rest/rest.py
  • test/ably/rest/restinit_test.py
🧰 Additional context used
🪛 Ruff (0.8.2)
test/ably/rest/restinit_test.py

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


84-84: Yoda condition detected

Rewrite as host == "sandbox.realtime.ably-nonprod.net"

(SIM300)


185-187: Yoda condition detected

(SIM300)


194-196: Yoda condition detected

(SIM300)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: check (3.13)
  • GitHub Check: check (3.12)
  • GitHub Check: check (3.11)
  • GitHub Check: check (3.10)
  • GitHub Check: check (3.9)
  • GitHub Check: check (3.8)
🔇 Additional comments (12)
test/unit/options_test.py (5)

6-14: LGTM! Thorough validation tests for incompatible client options.

The tests correctly verify that a ValueError is raised when endpoint is used together with any of the legacy options (environment, rest_host, or realtime_host). This aligns with the PR objective of making the endpoint option mutually exclusive with legacy options.


17-20: LGTM! Good verification of default hostname behavior.

Tests confirm that when no endpoint is specified, the default hosts are correctly set to "main.realtime.ably.net" with appropriate fallback hosts.


23-27: LGTM! Comprehensive test for routing policy hostnames.

Verifies that when a routing policy name (e.g., "foo") is provided as endpoint, the hostnames are correctly generated with the appropriate domain structure.


29-32: LGTM! Proper validation for nonprod routing policy hostnames.

Test correctly verifies that when a non-production routing policy (e.g., "nonprod:foo") is specified, the realtime host and fallback hosts use the nonprod domain structure.


35-38: LGTM! Good verification of FQDN endpoint handling.

Test confirms that when a fully qualified domain name is provided as endpoint, it's used directly as the realtime host and no fallback hosts are provided, which aligns with the ADR-119 requirements.

test/ably/rest/restinit_test.py (2)

82-84: Fix Yoda condition and update environment to endpoint.

Update the test to use endpoint instead of environment for consistency with ADR-119.

-        ably = AblyRest(token='foo', environment="nonprod:sandbox")
+        ably = AblyRest(token='foo', endpoint="nonprod:sandbox")
         host = ably.options.get_rest_host()
-        assert "sandbox.realtime.ably-nonprod.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "sandbox.realtime.ably-nonprod.net", "Unexpected host mismatch %s" % host
🧰 Tools
🪛 Ruff (0.8.2)

84-84: Yoda condition detected

Rewrite as host == "sandbox.realtime.ably-nonprod.net"

(SIM300)


106-107: Update environment to endpoint in fallback hosts test.

For consistency with ADR-119, update the test to use endpoint instead of environment.

-        ably = AblyRest(token='foo', environment='nonprod:sandbox', http_max_retry_count=10)
+        ably = AblyRest(token='foo', endpoint='nonprod:sandbox', http_max_retry_count=10)
         assert sorted(Defaults.get_fallback_hosts('nonprod:sandbox')) == sorted(
             ably.options.get_fallback_rest_hosts())
ably/transport/defaults.py (2)

47-57: LGTM! Well-implemented hostname resolution logic.

The hostname resolution logic correctly handles the different endpoint formats:

  1. FQDN when the endpoint contains a dot, double colon, or "localhost"
  2. Non-production routing policy names that start with "nonprod:"
  3. Default routing policy "main"
  4. Other routing policy names

This implementation aligns well with the ADR-119 requirements.


60-77:

Details

❓ Verification inconclusive

Implement REC2c2 fallback behavior.

While the fallback host generation is correctly implemented, the code doesn't fully implement the REC2c2 requirement for fallback behavior. According to REC2c2, when the primary host is unreachable, the SDK should:

  1. Iterate through fallback hosts in a shuffled order
  2. Implement a fallback retry timeout (default 10 minutes)
  3. Differentiate between production and non-production environments

From the Ably SDK specification, REC2c2 requires implementing a robust fallback mechanism that iterates through fallback hosts when the primary host is unreachable and implements a retry timeout before reverting to the primary host.

Please ensure this fallback behavior is fully implemented or confirm if it's handled elsewhere in the codebase.


🌐 Web query:

What is REC2c2 in Ably SDK specification?

💡 Result:

Based on the provided search results, there is no explicit mention or definition of "REC2c2" in the Ably SDK specification documents. The specification repository ([1]) details versioning protocols like:

  • Protocol Version (integer tracking wire protocol changes, specified under RSC7a for REST and RTN2f for Realtime connections)
  • Build Version (SemVer for SDK package versions)
  • Specification Version (SemVer for backward-compatibility tracking in SDK APIs)

The "REC2c2" identifier doesn’t appear in:

  1. Protocol/versioning tables
  2. Feature spec point tracking systems
  3. Connection state documentation ([5])
  4. Client library examples ([2][6][7])

This suggests it might either be:

  • A deprecated/legacy spec point identifier
  • A typographical error in the query
  • Part of internal documentation not included in public repositories

For authoritative clarification, consult the latest Ably SDK specification directly or contact Ably support with the exact context where this identifier was referenced.

Citations:


Clarify Fallback Behavior Implementation per REC2c2

  • The fallback URL generation implemented in this file appears to correctly construct the expected endpoints for both production and non-production environments.
  • However, the additional fallback behavior—as implied by REC2c2 (namely, iterating through fallback hosts in a shuffled order and enforcing a fallback retry timeout of 10 minutes)—is not apparent in this snippet.
  • Given that public Ably SDK documentation does not clearly define a "REC2c2" requirement, please confirm if:
    • The shuffled iteration and retry timeout logic are handled elsewhere in the codebase, or
    • They need to be implemented here to meet internal specifications.
ably/types/options.py (3)

22-24: LGTM! Good validation for incompatible options.

This validation correctly enforces the mutual exclusivity between the new endpoint option and legacy options (environment, rest_host, realtime_host) as required by ADR-119.

🧰 Tools
🪛 Ruff (0.8.2)

22-23: Use a single if statement instead of nested if statements

(SIM102)


53-60: LGTM! Good handling of environment and endpoint mapping.

The code correctly maps "production" environment to the default endpoint, preserves backward compatibility by mapping environment to endpoint when needed, and sets a default endpoint when none is provided.


176-177: LGTM! Property updated from environment to endpoint.

This change properly updates the property from environment to endpoint to align with the new connection options model.

@lawrence-forooghian
Copy link
Collaborator

@surminus as discussed on Slack, please could you make sure that this PR implements / tests the updates to REC1b2 from ably/specification#302? Thanks!

@github-actions github-actions bot temporarily deployed to staging/pull/590/features May 22, 2025 11:22 Inactive
@surminus
Copy link
Author

@lawrence-forooghian updated

@github-actions github-actions bot temporarily deployed to staging/pull/590/features May 22, 2025 11:24 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.

Actionable comments posted: 1

🔭 Outside diff range comments (1)
test/ably/rest/restinit_test.py (1)

212-220: 🛠️ Refactor suggestion

Update custom environment test to use endpoint parameter

For consistency with the new endpoint approach, update the test to use the endpoint parameter and avoid using the deprecated environment parameter.

-        ably = AblyRest(token='token', environment='custom')
+        ably = AblyRest(token='token', endpoint='custom')
         with patch.object(AsyncClient, 'send', wraps=ably.http._Http__client.send) as get_mock:
             try:
                 await ably.time()
             except AblyException:
                 pass
             request = get_mock.call_args_list[0][0][0]
-            assert request.url == 'https://custom.realtime.ably.net:443/time'
+            assert request.url == 'https://custom.realtime.ably.net:443/time'
🧰 Tools
🪛 Ruff (0.11.9)

215-218: Use contextlib.suppress(AblyException) instead of try-except-pass

Replace with contextlib.suppress(AblyException)

(SIM105)

♻️ Duplicate comments (3)
test/ably/rest/restinit_test.py (2)

82-84: 🛠️ Refactor suggestion

Use endpoint parameter instead of environment for nonprod:sandbox

Update the test to use endpoint instead of environment for consistency with ADR-119. Also fix the Yoda condition for better readability.

-        ably = AblyRest(token='foo', environment="nonprod:sandbox")
+        ably = AblyRest(token='foo', endpoint="nonprod:sandbox")
         host = ably.options.get_rest_host()
-        assert "sandbox.realtime.ably-nonprod.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "sandbox.realtime.ably-nonprod.net", "Unexpected host mismatch %s" % host
🧰 Tools
🪛 Ruff (0.11.9)

84-84: Yoda condition detected

Rewrite as host == "sandbox.realtime.ably-nonprod.net"

(SIM300)


106-108: 🛠️ Refactor suggestion

Update environment to endpoint in fallback hosts test

For consistency with ADR-119, update the test to use endpoint instead of environment.

-        ably = AblyRest(token='foo', environment='nonprod:sandbox', http_max_retry_count=10)
+        ably = AblyRest(token='foo', endpoint='nonprod:sandbox', http_max_retry_count=10)
         assert sorted(Defaults.get_fallback_hosts('nonprod:sandbox')) == sorted(
             ably.options.get_fallback_rest_hosts())
ably/types/options.py (1)

54-58: 🛠️ Refactor suggestion

Move validation check before setting default values

The incompatibility check between endpoint and other parameters should happen before line 40, as suggested in past review comments. This allows for early returns and improves code clarity.

-        if environment is not None and endpoint is None:
-            endpoint = environment
-
-        if endpoint is None:
-            endpoint = Defaults.endpoint
+        # Move this logic earlier in the method, before line 40
+        if endpoint is None:
+            if environment is not None:
+                endpoint = environment
+            else:
+                endpoint = Defaults.endpoint
🧹 Nitpick comments (3)
test/ably/rest/restinit_test.py (2)

185-187: Fix Yoda condition in HTTPS URL test

Improve readability by rewriting the assertion to avoid Yoda condition.

-        assert 'https://main.realtime.ably.net' == '{0}://{1}'.format(
-            ably.http.preferred_scheme, ably.http.preferred_host
-        )
+        assert '{0}://{1}'.format(
+            ably.http.preferred_scheme, ably.http.preferred_host
+        ) == 'https://main.realtime.ably.net'
🧰 Tools
🪛 Ruff (0.11.9)

185-187: Yoda condition detected

(SIM300)


194-196: Fix Yoda condition in HTTP URL test

Improve readability by rewriting the assertion to avoid Yoda condition.

-        assert 'http://main.realtime.ably.net' == '{0}://{1}'.format(
-            ably.http.preferred_scheme, ably.http.preferred_host
-        )
+        assert '{0}://{1}'.format(
+            ably.http.preferred_scheme, ably.http.preferred_host
+        ) == 'http://main.realtime.ably.net'
🧰 Tools
🪛 Ruff (0.11.9)

194-196: Yoda condition detected

(SIM300)

ably/types/options.py (1)

22-25: Simplify the validation check for endpoint incompatibility

The nested if statement can be simplified to improve readability, as suggested by the static analysis.

-        if endpoint is not None:
-            if environment is not None or rest_host is not None or realtime_host is not None:
-                raise ValueError('endpoint is incompatible with any of environment, rest_host or realtime_host')
+        if endpoint is not None and (environment is not None or rest_host is not None or realtime_host is not None):
+            raise ValueError('endpoint is incompatible with any of environment, rest_host or realtime_host')
🧰 Tools
🪛 Ruff (0.11.9)

22-23: Use a single if statement instead of nested if statements

(SIM102)

🛑 Comments failed to post (1)
test/ably/rest/restinit_test.py (1)

76-79: 🛠️ Refactor suggestion

Use endpoint parameter instead of environment for main environment

For consistency with the new endpoint-based connection approach in ADR-119, use the endpoint parameter instead of environment in the test. This better reflects the intended design changes.

-        ably = AblyRest(token='foo', environment="main")
+        ably = AblyRest(token='foo', endpoint="main")
         host = ably.options.get_rest_host()
-        assert "main.realtime.ably.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "main.realtime.ably.net", "Unexpected host mismatch %s" % host
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

        # environment: main
        ably = AblyRest(token='foo', endpoint="main")
        host = ably.options.get_rest_host()
        assert host == "main.realtime.ably.net", "Unexpected host mismatch %s" % host
🧰 Tools
🪛 Ruff (0.11.9)

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)

🤖 Prompt for AI Agents
In test/ably/rest/restinit_test.py around lines 76 to 79, replace the use of the
environment parameter with the endpoint parameter when creating the AblyRest
instance. Change the constructor call to use endpoint="main" instead of
environment="main" to align with the new endpoint-based connection approach
introduced in ADR-119.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
test/ably/rest/restinit_test.py (2)

82-84: Use endpoint instead of environment and fix Yoda condition.

For consistency with ADR-119, update to use the new endpoint parameter rather than environment. Also, reverse the condition to follow standard Python comparison style.

-        ably = AblyRest(token='foo', environment="nonprod:sandbox")
+        ably = AblyRest(token='foo', endpoint="nonprod:sandbox")
         host = ably.options.get_rest_host()
-        assert "sandbox.realtime.ably-nonprod.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "sandbox.realtime.ably-nonprod.net", "Unexpected host mismatch %s" % host
🧰 Tools
🪛 Ruff (0.11.9)

84-84: Yoda condition detected

Rewrite as host == "sandbox.realtime.ably-nonprod.net"

(SIM300)


106-108: Update environment to endpoint in fallback hosts test.

For consistency with ADR-119, update the test to use endpoint instead of environment.

-        ably = AblyRest(token='foo', environment='nonprod:sandbox', http_max_retry_count=10)
+        ably = AblyRest(token='foo', endpoint='nonprod:sandbox', http_max_retry_count=10)
         assert sorted(Defaults.get_fallback_hosts('nonprod:sandbox')) == sorted(
             ably.options.get_fallback_rest_hosts())
🧹 Nitpick comments (10)
test/ably/rest/restinit_test.py (6)

76-79: Use endpoint instead of environment parameter.

This test is still using the deprecated environment parameter instead of the new endpoint parameter introduced in ADR-119. While this works due to backward compatibility, it doesn't test the intended new API.

-        ably = AblyRest(token='foo', environment="main")
+        ably = AblyRest(token='foo', endpoint="main")
         host = ably.options.get_rest_host()
-        assert "main.realtime.ably.net" == host, "Unexpected host mismatch %s" % host
+        assert host == "main.realtime.ably.net", "Unexpected host mismatch %s" % host
🧰 Tools
🪛 Ruff (0.11.9)

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


112-112: Explicitly test default endpoint.

The code is calling Defaults.get_fallback_hosts() without arguments, which uses the default endpoint. For clarity, consider explicitly passing the default endpoint value from Defaults.endpoint to make the test intention more obvious.

-        assert sorted(Defaults.get_fallback_hosts()) == sorted(ably.options.get_fallback_rest_hosts())
+        assert sorted(Defaults.get_fallback_hosts(Defaults.endpoint)) == sorted(ably.options.get_fallback_rest_hosts())

185-187: Fix Yoda condition in URL assertion.

Rewrite the condition to follow standard Python comparison style.

-        assert 'https://main.realtime.ably.net' == '{0}://{1}'.format(
+        assert '{0}://{1}'.format(
             ably.http.preferred_scheme, ably.http.preferred_host
-        )
+        ) == 'https://main.realtime.ably.net'
🧰 Tools
🪛 Ruff (0.11.9)

185-187: Yoda condition detected

(SIM300)


194-196: Fix Yoda condition in HTTP URL assertion.

Rewrite the condition to follow standard Python comparison style.

-        assert 'http://main.realtime.ably.net' == '{0}://{1}'.format(
+        assert '{0}://{1}'.format(
             ably.http.preferred_scheme, ably.http.preferred_host
-        )
+        ) == 'http://main.realtime.ably.net'
🧰 Tools
🪛 Ruff (0.11.9)

194-196: Yoda condition detected

(SIM300)


212-220: Use endpoint instead of environment for custom environment test.

This test is still using the deprecated environment parameter instead of the new endpoint parameter. Update it to test the intended new API.

-    async def test_environment(self):
-        ably = AblyRest(token='token', environment='custom')
+    async def test_endpoint(self):
+        ably = AblyRest(token='token', endpoint='custom')
         with patch.object(AsyncClient, 'send', wraps=ably.http._Http__client.send) as get_mock:
             try:
                 await ably.time()
             except AblyException:
                 pass
             request = get_mock.call_args_list[0][0][0]
             assert request.url == 'https://custom.realtime.ably.net:443/time'
🧰 Tools
🪛 Ruff (0.11.9)

215-218: Use contextlib.suppress(AblyException) instead of try-except-pass

Replace with contextlib.suppress(AblyException)

(SIM105)


14-224: Add explicit tests for the new endpoint functionality.

The test suite is missing explicit tests for several key features of the endpoint implementation as specified in ADR-119:

  1. FQDN endpoints (e.g., "foo.example.com")
  2. Validation of incompatible option combinations
  3. Default fallback behavior for different endpoint types

Consider adding the following test cases:

@dont_vary_protocol
def test_endpoint_fqdn():
    # Test FQDN as endpoint
    ably = AblyRest(token='foo', endpoint="foo.example.com")
    host = ably.options.get_rest_host()
    assert host == "foo.example.com", "Unexpected host mismatch %s" % host
    # No fallback hosts for FQDN endpoints
    assert ably.options.get_fallback_rest_hosts() == []

@dont_vary_protocol
def test_endpoint_incompatible_options():
    # Test endpoint with environment
    with pytest.raises(ValueError, match="endpoint is incompatible"):
        AblyRest(token='foo', endpoint="main", environment="sandbox")
    
    # Test endpoint with rest_host
    with pytest.raises(ValueError, match="endpoint is incompatible"):
        AblyRest(token='foo', endpoint="main", rest_host="some.other.host")
    
    # Test endpoint with realtime_host
    with pytest.raises(ValueError, match="endpoint is incompatible"):
        AblyRest(token='foo', endpoint="main", realtime_host="some.other.host")
🧰 Tools
🪛 Ruff (0.11.9)

74-74: Yoda condition detected

Rewrite as ably.options.rest_host == "some.other.host"

(SIM300)


79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


84-84: Yoda condition detected

Rewrite as host == "sandbox.realtime.ably-nonprod.net"

(SIM300)


116-116: Yoda condition detected

Rewrite as ably.options.fallback_retry_timeout == 600000

(SIM300)


118-118: Yoda condition detected

Rewrite as ably.options.fallback_retry_timeout == 1000

(SIM300)


123-123: Yoda condition detected

Rewrite as ably.options.realtime_host == "some.other.host"

(SIM300)


128-128: Yoda condition detected

Rewrite as Defaults.get_port(ably.options) == 9999

(SIM300)


134-134: Yoda condition detected

Rewrite as Defaults.get_port(ably.options) == 9998

(SIM300)


140-140: Yoda condition detected

Rewrite as Defaults.get_port(ably.options) == 9999

(SIM300)


185-187: Yoda condition detected

(SIM300)


194-196: Yoda condition detected

(SIM300)


207-207: Yoda condition detected

Rewrite as excinfo.value.status_code == 401

(SIM300)


208-208: Yoda condition detected

Rewrite as excinfo.value.code == 40103

(SIM300)


209-209: Yoda condition detected

(SIM300)


215-218: Use contextlib.suppress(AblyException) instead of try-except-pass

Replace with contextlib.suppress(AblyException)

(SIM105)

ably/types/options.py (4)

54-58: Consider moving the endpoint initialization closer to the validation logic.

For better code organization and readability, consider moving these lines up to follow immediately after the incompatible options check. This will group all endpoint-related logic together.

# Move these lines to follow line 25
+        if environment is not None and endpoint is None:
+            endpoint = environment
+
+        if endpoint is None:
+            endpoint = Defaults.endpoint

174-176: Add setter for endpoint property.

Most properties in this class have setters, but the endpoint property does not. For consistency and to support runtime changes to the endpoint, consider adding a setter.

    @property
    def endpoint(self):
        return self.__endpoint

+    @endpoint.setter
+    def endpoint(self, value):
+        self.__endpoint = value
+        # Update hosts when endpoint changes
+        self.__rest_hosts = self.__get_rest_hosts()
+        self.__realtime_hosts = self.__get_realtime_hosts()

288-291: Simplify the conditional logic for fallback hosts.

The current condition checks if rest_host is not None, but the variable name doesn't match the property name used elsewhere (self.rest_host). Make sure the condition uses the correct property access for consistency.

-            if self.rest_host is not None:
+            if self.rest_host is not None:
                 fallback_hosts = []
-            else:
+            else:
                 fallback_hosts = Defaults.get_fallback_hosts(self.endpoint)

303-309: Consider reusing get_rest_hosts logic for get_realtime_hosts.

The __get_realtime_hosts method duplicates some of the logic from __get_rest_hosts. Consider refactoring to eliminate this duplication.

    def __get_realtime_hosts(self):
        if self.realtime_host is not None:
            host = self.realtime_host
            return [host]

-        host = Defaults.get_hostname(self.endpoint)
-        return [host] + self.__fallback_hosts
+        # Reuse the same hostname logic as rest hosts
+        if self.rest_host is not None:
+            # If rest_host is specified but realtime_host isn't, don't use fallbacks
+            # This matches the behavior for rest hosts
+            return [Defaults.get_hostname(self.endpoint)]
+        else:
+            # Use the same host logic and fallbacks as rest
+            host = Defaults.get_hostname(self.endpoint)
+            return [host] + self.__fallback_hosts
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro (Legacy)

📥 Commits

Reviewing files that changed from the base of the PR and between 2ec3c21 and ce562e7.

📒 Files selected for processing (6)
  • ably/realtime/realtime.py (1 hunks)
  • ably/rest/rest.py (1 hunks)
  • ably/transport/defaults.py (2 hunks)
  • ably/types/options.py (6 hunks)
  • test/ably/rest/restinit_test.py (4 hunks)
  • test/unit/options_test.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • ably/rest/rest.py
  • ably/realtime/realtime.py
  • test/unit/options_test.py
  • ably/transport/defaults.py
🧰 Additional context used
🧬 Code Graph Analysis (2)
ably/types/options.py (2)
ably/http/http.py (4)
  • http_open_timeout (281-284)
  • http_request_timeout (287-290)
  • http_max_retry_count (293-296)
  • http_max_retry_duration (299-302)
ably/transport/defaults.py (3)
  • Defaults (1-77)
  • get_hostname (47-54)
  • get_fallback_hosts (57-77)
test/ably/rest/restinit_test.py (5)
ably/rest/rest.py (3)
  • options (121-122)
  • http (117-118)
  • request (128-148)
ably/types/options.py (6)
  • get_rest_host (314-315)
  • http_max_retry_count (198-199)
  • http_max_retry_count (202-203)
  • get_fallback_rest_hosts (323-324)
  • tls (109-110)
  • tls (113-114)
ably/transport/defaults.py (2)
  • Defaults (1-77)
  • get_fallback_hosts (57-77)
ably/http/http.py (4)
  • preferred_scheme (277-278)
  • preferred_host (269-270)
  • preferred_port (273-274)
  • url (67-68)
test/ably/utils.py (1)
  • dont_vary_protocol (156-158)
🪛 Ruff (0.11.9)
ably/types/options.py

22-23: Use a single if statement instead of nested if statements

(SIM102)

test/ably/rest/restinit_test.py

79-79: Yoda condition detected

Rewrite as host == "main.realtime.ably.net"

(SIM300)


84-84: Yoda condition detected

Rewrite as host == "sandbox.realtime.ably-nonprod.net"

(SIM300)


185-187: Yoda condition detected

(SIM300)


194-196: Yoda condition detected

(SIM300)

⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: check (3.13)
  • GitHub Check: check (3.11)
  • GitHub Check: check (3.10)
  • GitHub Check: check (3.12)
  • GitHub Check: check (3.8)
  • GitHub Check: check (3.9)
  • GitHub Check: check (3.7)

@github-actions github-actions bot temporarily deployed to staging/pull/590/features January 19, 2026 11:19 Inactive
This implements ADR-119[1], which specifies the client connection
options to update requests to the endpoints implemented as part of
ADR-042[2].

The endpoint may be one of the following:

* a routing policy name (such as main)
* a nonprod routing policy name (such as nonprod:sandbox)
* a FQDN such as foo.example.com

The endpoint option is not valid with any of environment, restHost or
realtimeHost, but we still intend to support the legacy options.

If the client has been configured to use any of these legacy options,
then they should continue to work in the same way, using the same
primary and fallback hostnames.

If the client has not been explicitly configured, then the hostnames
will change to the new ably.net domain when the package is upgraded.

[1] https://ably.atlassian.net/wiki/spaces/ENG/pages/3428810778/ADR-119+ClientOptions+for+new+DNS+structure
[2] https://ably.atlassian.net/wiki/spaces/ENG/pages/1791754276/ADR-042+DNS+Restructure
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ably/types/options.py (1)

304-324: Remove undefined environment prefixing in REST host selection.

environment is no longer defined in this class, so this block raises a NameError and also reintroduces legacy behavior that conflicts with endpoint-based hostnames. Remove it.

🛠️ Proposed fix
-        # Prepend environment
-        if environment != 'production':
-            host = f'{environment}-{host}'

@github-actions github-actions bot temporarily deployed to staging/pull/590/features January 19, 2026 11:24 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@ably/realtime/realtime.py`:
- Around line 51-59: The docstring for the realtime connection parameters
incorrectly states defaults for the deprecated realtime_host and environment;
update the docstring for the realtime function/class so it clarifies these are
derived from the endpoint: explain that when realtime_host is unset the hostname
is resolved from endpoint via Defaults.get_hostname() (so e.g. the default
endpoint 'main' maps to 'main.realtime.ably.net'), and that environment defaults
to the endpoint value (typically 'main'), and mark both realtime_host and
environment as deprecated and endpoint-driven; reference the realtime_host and
environment parameter entries and Defaults.get_hostname() in your updated text.
♻️ Duplicate comments (1)
ably/types/options.py (1)

72-76: Backward compatibility mapping from environment to endpoint is correct.

The logic properly supports legacy environment parameter while transitioning to endpoint. Consider adding deprecation warnings for environment, rest_host, and realtime_host as mentioned in past reviews to guide users toward the new API.

🧹 Nitpick comments (2)
ably/transport/defaults.py (1)

61-62: Consider using consistent string slicing for nonprod prefix removal.

Line 52 uses endpoint[len("nonprod:"):] while line 62 uses endpoint.replace("nonprod:", ""). While both work correctly given the startswith guard, using the same approach would improve consistency.

♻️ Suggested change
         if endpoint.startswith("nonprod:"):
-            root = endpoint.replace("nonprod:", "")
+            root = endpoint[len("nonprod:"):]
             return [
test/ably/testapp.py (1)

20-20: Variable naming mismatch between env var and usage.

The environment variable is now ABLY_ENDPOINT (line 20), but the local variable is still named environment and passed as environment=environment (line 33). While this works due to backward compatibility in Options, the naming is misleading.

Consider renaming to clarify intent:

♻️ Suggested rename for clarity
-environment = os.environ.get('ABLY_ENDPOINT', 'nonprod:sandbox')
+endpoint = os.environ.get('ABLY_ENDPOINT', 'nonprod:sandbox')

And update line 33 accordingly:

 ably = AblyRest(token='not_a_real_token',
                 port=port, tls_port=tls_port, tls=tls,
-                environment=environment,
+                endpoint=endpoint,
                 use_binary_protocol=False)

Similar updates would be needed in get_options (line 91) and test_vars (line 56).

Also applies to: 33-33

@github-actions github-actions bot temporarily deployed to staging/pull/590/features January 20, 2026 18:14 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/590/features January 20, 2026 18:19 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/590/features January 20, 2026 19:25 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/590/features January 20, 2026 19:35 Inactive
@github-actions github-actions bot temporarily deployed to staging/pull/590/features January 20, 2026 19:37 Inactive
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ably/http/http.py (1)

143-158: Avoid ValueError when fallback host isn’t in the host list.

hosts.remove(host) will throw if self.__host/options.fallback_host isn’t present (e.g., list truncated by http_max_retry_count, or a stale fallback host). This would break all requests.

🛠️ Defensive fix
         hosts = list(hosts)
-        hosts.remove(host)
-        hosts.insert(0, host)
+        if host in hosts:
+            hosts.remove(host)
+            hosts.insert(0, host)
         return hosts
🤖 Fix all issues with AI agents
In `@ably/types/options.py`:
- Around line 320-325: The get_host method can raise IndexError when
self.__hosts is empty; update it to guard against empty host lists by checking
self.__hosts (used in get_hosts and get_host) and raising a clear exception
(e.g., ValueError with a descriptive message) or returning a sensible default
when empty; ensure any upstream validation of http_max_retry_count (where
__hosts is built) is considered and reference get_host/get_hosts/__hosts in the
change so callers receive a predictable error instead of an IndexError.
🧹 Nitpick comments (1)
test/ably/testapp.py (1)

92-102: Unused Options object in clear_test_vars.

The Options object created on line 95 and configured on lines 96-98 is never used. The ably client is created via get_ably_rest() on line 99, which constructs its own options internally. This appears to be pre-existing dead code, but worth cleaning up.

♻️ Suggested cleanup
     `@staticmethod`
     async def clear_test_vars():
         test_vars = TestApp.__test_vars
-        options = Options(key=test_vars["keys"][0]["key_str"])
-        options.port = test_vars["port"]
-        options.tls_port = test_vars["tls_port"]
-        options.tls = test_vars["tls"]
         ably = await TestApp.get_ably_rest()
         await ably.http.delete('/apps/' + test_vars['app_id'])
         TestApp.__test_vars = None
         await ably.close()

Comment on lines 320 to 325
def get_hosts(self):
return self.__hosts

def get_realtime_hosts(self):
return self.__realtime_hosts
def get_host(self):
return self.__hosts[0]

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Guard against empty host lists in get_host.

If http_max_retry_count is set to 0 (or other invalid values), __hosts can be empty and this will raise an IndexError. Consider validating the retry count or raising a clearer error here.

🩹 Suggested guard
 def get_host(self):
-        return self.__hosts[0]
+        if not self.__hosts:
+            raise ValueError("No hosts available; check http_max_retry_count and endpoint")
+        return self.__hosts[0]
🤖 Prompt for AI Agents
In `@ably/types/options.py` around lines 320 - 325, The get_host method can raise
IndexError when self.__hosts is empty; update it to guard against empty host
lists by checking self.__hosts (used in get_hosts and get_host) and raising a
clear exception (e.g., ValueError with a descriptive message) or returning a
sensible default when empty; ensure any upstream validation of
http_max_retry_count (where __hosts is built) is considered and reference
get_host/get_hosts/__hosts in the change so callers receive a predictable error
instead of an IndexError.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@test/ably/realtime/realtimeconnection_test.py`:
- Around line 347-348: The assertions are comparing transport.host and
options.fallback_host to test_vars["endpoint"] but should compare to
test_vars["host"]; update the two assertions in realtimeconnection_test.py so
ably.connection.connection_manager.transport.host != self.test_vars["host"] and
ably.options.fallback_host != self.test_vars["host"] (referencing transport.host
and options.fallback_host to locate the lines).
- Around line 302-303: The two assertions are comparing hostname values against
the wrong test var; update them to use self.test_vars["host"] instead of
self.test_vars["endpoint"]. Specifically locate the lines asserting
ably.connection.connection_manager.transport.host and ably.options.fallback_host
and change the RHS to self.test_vars["host"] so both comparisons match the
resolved hostname from Defaults.get_hostname(endpoint).

In `@test/ably/rest/resthttp_test.py`:
- Line 218: The test passes a full URL as the endpoint to TestApp.get_ably_rest
which leads Defaults.get_hostname() to build a malformed base_url (e.g.,
"https://https://..."); change the test to pass only the hostname (e.g.,
endpoint='www.example.com') or otherwise construct the mocked request using the
full URL explicitly instead of feeding a scheme-containing URL into
TestApp.get_ably_rest so that the base_url construction in
Defaults.get_hostname() remains correct.
🧹 Nitpick comments (2)
ably/types/options.py (2)

73-85: Deprecation uses logging instead of warnings.warn.

The implementation uses log.warning() for deprecation notices rather than the standard warnings.warn() with DeprecationWarning. While this works, using warnings.warn() would be more idiomatic for deprecations as it integrates with Python's warning filters and test frameworks that capture deprecation warnings.

This is a minor consideration - the current approach is functional and provides visibility to users.


323-324: Consider guarding against empty hosts list.

If http_max_retry_count is set to 0, __hosts will be empty and get_host() will raise an IndexError. Consider adding a guard or clearer error message.

🛡️ Suggested guard
 def get_host(self):
+    if not self.__hosts:
+        raise ValueError("No hosts available; check http_max_retry_count and endpoint configuration")
     return self.__hosts[0]

Comment on lines +302 to +303
assert ably.connection.connection_manager.transport.host != self.test_vars["endpoint"]
assert ably.options.fallback_host != self.test_vars["endpoint"]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check the actual assertion at lines 302-303
sed -n '295,310p' test/ably/realtime/realtimeconnection_test.py

Repository: ably/ably-python

Length of output: 854


🏁 Script executed:

# Find and examine the test_vars structure in testapp.py
rg -n -B2 -A10 'test_vars\s*=' test/ably/testapp.py | head -50

Repository: ably/ably-python

Length of output: 1773


🏁 Script executed:

# Search for how test_vars is populated and what keys are set
rg -n 'test_vars\[' test/ably/testapp.py | head -20

Repository: ably/ably-python

Length of output: 576


Use test_vars["host"] instead of test_vars["endpoint"] in assertions.

The assertions compare transport.host and fallback_host against test_vars["endpoint"], but these are semantically different values. The test_vars dictionary contains both:

  • "endpoint": the endpoint name (e.g., "nonprod:sandbox")
  • "host": the resolved hostname from Defaults.get_hostname(endpoint)

The comparison should use test_vars["host"] to match the hostname values being tested.

🤖 Prompt for AI Agents
In `@test/ably/realtime/realtimeconnection_test.py` around lines 302 - 303, The
two assertions are comparing hostname values against the wrong test var; update
them to use self.test_vars["host"] instead of self.test_vars["endpoint"].
Specifically locate the lines asserting
ably.connection.connection_manager.transport.host and ably.options.fallback_host
and change the RHS to self.test_vars["host"] so both comparisons match the
resolved hostname from Defaults.get_hostname(endpoint).

Comment on lines +347 to +348
assert ably.connection.connection_manager.transport.host != self.test_vars["endpoint"]
assert ably.options.fallback_host != self.test_vars["endpoint"]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Same hostname vs endpoint comparison concern as lines 302-303.

This assertion has the same potential issue - comparing transport.host against test_vars["endpoint"] when it should likely compare against test_vars["host"].

🤖 Prompt for AI Agents
In `@test/ably/realtime/realtimeconnection_test.py` around lines 347 - 348, The
assertions are comparing transport.host and options.fallback_host to
test_vars["endpoint"] but should compare to test_vars["host"]; update the two
assertions in realtimeconnection_test.py so
ably.connection.connection_manager.transport.host != self.test_vars["host"] and
ably.options.fallback_host != self.test_vars["host"] (referencing transport.host
and options.fallback_host to locate the lines).

respx.get(url).mock(return_value=Response(status_code=200))

ably = await TestApp.get_ably_rest(rest_host=url)
ably = await TestApp.get_ably_rest(endpoint=url)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Step 1: Look for the Defaults.get_hostname() implementation
echo "=== Searching for get_hostname implementation ==="
rg -n "def get_hostname" --type py -A 10

echo ""
echo "=== Searching for endpoint processing in options/defaults ==="
# Check if there's URL parsing or scheme stripping
rg -n "scheme|endpoint" ably/types/options.py ably/transport/defaults.py --type py | head -50

echo ""
echo "=== Check what get_ably_rest does with the endpoint ==="
rg -n "get_ably_rest" test/ably/rest/ --type py -A 5

Repository: ably/ably-python

Length of output: 36608


🏁 Script executed:

#!/bin/bash
# Step 2: Look for the URL parsing logic more carefully
echo "=== Search for urlparse or URL parsing in defaults ==="
rg -n "urlparse|urllib|parse" ably/ --type py

echo ""
echo "=== Check how RestClient or similar uses endpoint ==="
rg -n "class.*Rest.*Client\|endpoint\s*=" ably/rest/ --type py | head -30

Repository: ably/ably-python

Length of output: 2077


🏁 Script executed:

#!/bin/bash
# Step 3: Examine the specific test context
echo "=== Check the test file around line 218 ==="
sed -n '210,230p' test/ably/rest/resthttp_test.py

Repository: ably/ably-python

Length of output: 610


🏁 Script executed:

#!/bin/bash
# Find how get_host() or get_hostname() result is used
echo "=== Search for get_host method in Options ==="
rg -n "def get_host" --type py -A 8

echo ""
echo "=== Check how endpoint/host is used in HTTP client ==="
rg -n "get_host\|get_hostname" --type py | grep -v test | grep -v "def get_"

echo ""
echo "=== Look at how requests are built with the host ==="
rg -n "base_url\|http://" ably/http/ --type py -B 2 -A 2

Repository: ably/ably-python

Length of output: 2594


🏁 Script executed:

#!/bin/bash
# Check if there are other tests passing full URLs as endpoint
echo "=== Search for other endpoint= uses with full URLs ==="
rg -n 'endpoint=["\x27]https?://' test/ --type py

Repository: ably/ably-python

Length of output: 114


🏁 Script executed:

#!/antml:bash
# Look at TestApp.get_ably_rest to see how endpoint is used
echo "=== Find TestApp.get_ably_rest implementation ==="
fd -type f -name "*.py" -path "*/test/*" -exec grep -l "class TestApp\|def get_ably_rest" {} \;

Repository: ably/ably-python

Length of output: 297


🏁 Script executed:

#!/bin/bash
# Find TestApp implementation
echo "=== Find TestApp class ==="
find test -name "*.py" -type f | xargs grep -l "class TestApp" | head -1

echo ""
echo "=== Look at HTTP client URL construction ==="
rg -n "base_url\|scheme\|netloc" ably/http/http.py -A 3 -B 3 | head -80

Repository: ably/ably-python

Length of output: 153


🏁 Script executed:

#!/bin/bash
# Check how hosts are constructed and used in HTTP
echo "=== Search for __hosts initialization ==="
rg -n "__hosts\s*=" ably/types/options.py -A 2 -B 2

echo ""
echo "=== Check make_request to see how URL is constructed ==="
rg -n "def make_request\|base_url\|http://" ably/http/http.py -A 5 | head -100

Repository: ably/ably-python

Length of output: 340


🏁 Script executed:

#!/bin/bash
# Look at the actual URL construction in the HTTP layer
echo "=== Look at get_url method in HTTP ==="
rg -n "get_url\|urljoin\|scheme" ably/ --type py | grep -v test | head -30

Repository: ably/ably-python

Length of output: 98


🏁 Script executed:

#!/bin/bash
# Get more context on __get_hosts and how it builds the hosts
echo "=== Find __get_hosts method in Options ==="
rg -n "def __get_hosts" ably/types/options.py -A 15

echo ""
echo "=== Look at HTTP make_request method implementation ==="
rg -n "async def make_request\|def make_request" ably/http/http.py -A 20

Repository: ably/ably-python

Length of output: 857


🏁 Script executed:

#!/bin/bash
# Check how base_url is constructed in HTTP client
echo "=== Search for where HTTP client builds URLs ==="
rg -n "httpx\|url\s*=\|base_url" ably/http/http.py | head -40

echo ""
echo "=== Look at httpx client initialization ==="
rg -n "AsyncClient\|httpx" ably/http/http.py -B 2 -A 5

Repository: ably/ably-python

Length of output: 153


🏁 Script executed:

#!/bin/bash
# Read the HTTP client file to understand URL construction
echo "=== Reading HTTP client implementation ==="
head -200 ably/http/http.py | tail -100

echo ""
echo "=== Look for URL construction patterns ==="
sed -n '1,50p' ably/http/http.py

Repository: ably/ably-python

Length of output: 5129


🏁 Script executed:

#!/bin/bash
# Search more specifically for how requests are made
echo "=== Find request construction in http.py ==="
cat ably/http/http.py | grep -n "url\|http://" | head -30

Repository: ably/ably-python

Length of output: 1685


🏁 Script executed:

#!/antml/bash
# Look at actual test to see what's expected
echo "=== Check the full test context again ==="
sed -n '214,225p' test/ably/rest/resthttp_test.py

echo ""
echo "=== Look at TestApp implementation ==="
head -100 test/ably/testapp.py

Repository: ably/ably-python

Length of output: 3802


Remove the scheme from the endpoint or pass only the hostname.

Passing a full URL with scheme as the endpoint parameter causes malformed request URLs. The endpoint parameter is intended for hostnames only. When processed by Defaults.get_hostname(), it's incorporated into a base_url string as f"{scheme}://{host}:{port}", which with a full URL like 'https://www.example.com' produces 'https://https://www.example.com:443'. The test works only because it passes an absolute URL as the request path, which overrides the malformed base URL via urljoin(). Normal API calls with relative paths would fail. Either pass endpoint='www.example.com' or construct the full URL differently for mocking purposes.

🤖 Prompt for AI Agents
In `@test/ably/rest/resthttp_test.py` at line 218, The test passes a full URL as
the endpoint to TestApp.get_ably_rest which leads Defaults.get_hostname() to
build a malformed base_url (e.g., "https://https://..."); change the test to
pass only the hostname (e.g., endpoint='www.example.com') or otherwise construct
the mocked request using the full URL explicitly instead of feeding a
scheme-containing URL into TestApp.get_ably_rest so that the base_url
construction in Defaults.get_hostname() remains correct.

Copy link
Member

@owenpearson owenpearson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly looks good, but couple of issues

# REC1b1: endpoint is incompatible with deprecated options
if endpoint is not None:
if environment is not None or rest_host is not None or realtime_host is not None:
raise ValueError('endpoint is incompatible with any of environment, rest_host or realtime_host')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be an errorinfo with code 40106

self.__http_max_retry_count = http_max_retry_count
self.__http_max_retry_duration = http_max_retry_duration
# Field for internal use only
self.__fallback_host = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this immediately gets overwritten in the next line?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

7 participants